package com.hsgene.order.service.impl;

import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.Producer;
import com.aliyun.openservices.ons.api.SendResult;
import com.hsgene.common.ClientType;
import com.hsgene.common.DeleteFlagType;
import com.hsgene.common.util.basic.JsonUtils;
import com.hsgene.common.util.basic.UUID32;
import com.hsgene.common.util.date.SimpleDateUtils;
import com.hsgene.common.util.result.ApiResult;
import com.hsgene.common.util.result.NullObject;
import com.hsgene.common.util.validation.FieldChecker;
import com.hsgene.order.constants.v1.OrderPackageStatus;
import com.hsgene.order.constants.v1.PostponeFlag;
import com.hsgene.order.domain.v1.GeneticTestingOrder;
import com.hsgene.order.domain.v1.GeneticTestingOrderPackage;
import com.hsgene.order.dto.message.OrderPostponeMessageDto;
import com.hsgene.order.exception.OrderErrorCode;
import com.hsgene.order.persistence.OrderPackageRepository;
import com.hsgene.order.persistence.OrderRepository;
import com.hsgene.order.service.OrderPostponeService;
import com.hsgene.order.service.api.ProductService;
import com.hsgene.product.constants.PeriodType;
import com.hsgene.product.domain.GeneticTestingPackage;
import com.hsgene.product.domain.GeneticTestingProductDetail;
import com.hsgene.util.mq.rocket.constants.RocketMQConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * 订单超期处理Service
 *
 * @author wangbing
 * @version 1.0
 * @date 2019/1/18 17:10
 * Copyright: Copyright (c) 2019
 */
@Service
public class OrderPostponeServiceImpl implements OrderPostponeService {

    private final static Logger LOGGER = LoggerFactory.getLogger("OrderPostponeServiceImpl");

    @Resource
    private OrderRepository orderRepository;

    @Resource
    private OrderPackageRepository orderPackageRepository;

    @Resource
    private Producer producer;

    @Resource
    private ProductService productService;

    @Value("${mq.rocket.topic.genetic_testing_order}")
    private String postponeTopic;

    @Value("${mq.rocket.tag.genetic_testing_order_postpone}")
    private String postponeTag;

    @Value("${mq.rocket.debug:false}")
    private Boolean mqRocketDebug;

    @Override
    public void orderSampleConfirmed(String orderId, String packageId) {
        FieldChecker.checkEmpty(orderId, "订单id");
        FieldChecker.checkEmpty(packageId, "套餐id");

        GeneticTestingOrderPackage orderPackage = orderPackageRepository.findOrderPackage(orderId, packageId);
        // 订单套餐存在
        if (orderPackage == null) {
            LOGGER.error("订单[{}]套餐[{}]不存在", orderId, packageId);
            throw new IllegalStateException("订单套餐不存在");
        }
        // 状态为待上传报告
        OrderPackageStatus status = OrderPackageStatus.getEnum(orderPackage.getStatus());
        if (!OrderPackageStatus.REPORT_UPLOAD.equals(status)) {
            LOGGER.error("订单[{}]套餐[{}:{}]非报告待上传状态，不能开启检测报告超期提醒", orderId, packageId, status.getDesc());
            throw new IllegalStateException("订单套餐非报告待上传状态");
        }
        // 查询套餐详情
        ApiResult<GeneticTestingPackage> packageResult = productService.getPackageById(packageId, ClientType.CONSOLE.getCode());
        if (!packageResult.isSucc()) {
            LOGGER.error("获取订单[{}]套餐[{}]信息失败！{}", orderId, packageId, packageResult.getDesc());
            throw new IllegalStateException(packageResult.getDesc());
        }
        GeneticTestingPackage testingPackage = packageResult.getData();
        Optional<GeneticTestingProductDetail> agency = testingPackage.getGeneticTestingProduct().getTestingProductDetailList().stream()
                .filter(e -> e.getAgencyId().equals(orderPackage.getGeneticTestingAgencyId())).findFirst();
        if (!agency.isPresent()) {
            LOGGER.error("没有找到订单[{}]套餐[{}]的机构[{}]检测信息", orderId, packageId, orderPackage.getGeneticTestingAgencyId());
            throw new IllegalStateException("订单套餐的机构检测信息不存在");
        }
        GeneticTestingProductDetail productDetail = agency.get();
        // 检测周期类型：0-工作日；1-自然日
        Integer periodType = PeriodType.getEnum(productDetail.getDetectionPeriodUnit()).getCode();
        Integer period = Integer.valueOf(productDetail.getDetectionPeriod());
        OrderPostponeMessageDto message = new OrderPostponeMessageDto(orderId, packageId);
        message.setPostponeTimestamp(calculateDeliverTime(periodType, period));
        // 发送消息
        sendPostponeMessage(message);
    }

    /**
     * 发送超期检测消息
     *
     * @param message 消息内容
     */
    private void sendPostponeMessage(OrderPostponeMessageDto message) {
        String msgBody = JsonUtils.objectToJson(message);
        Message msg = new Message(postponeTopic, postponeTag, msgBody.getBytes());
        // 检测时长超过40天的需要特殊处理
        msg.setStartDeliverTime(calculateDeliverTime(message));
        msg.setKey("postpone-" + UUID32.randomUUIDString());
        try {
            SendResult sendResult = producer.send(msg);
            LOGGER.info("订单[{}]套餐[{}]检测超期定时消息提醒发送成功！SendResult = {}, MSGBody = {}", message.getOrderId(), message.getPackageId(), sendResult, msgBody);
        } catch (Exception e) {
            LOGGER.error("订单[{}]套餐[{}]检测超期定时消息提醒发送失败！", message.getOrderId(), message.getPackageId());
            e.printStackTrace();
            throw new IllegalStateException("订单套餐检测超期定时消息提醒发送失败!");
        }
    }

    /**
     * 计算消息发送时间
     *
     * @param periodType 周期类型
     * @param period     周期
     * @return 发送时间戳
     */
    private long calculateDeliverTime(Integer periodType, Integer period) {
        LocalDateTime deliverTime;
        LocalDate deadline = periodType == 1 ? SimpleDateUtils.plusNaturalDay(period) : SimpleDateUtils.plusWordDay(period);
        deliverTime = LocalDateTime.of(deadline, LocalTime.MIN);
        // 开发测试环境统一缩短时间（策略：把天变为分钟），方便开发调试与测试人员测试
        if (isDebug()) {
            int days = SimpleDateUtils.durationDays(LocalDateTime.now(), deliverTime);
            deliverTime = LocalDateTime.now().plusMinutes(days);
        }

        return deliverTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
    }

    /**
     * 根据消息内容中检测超时时间戳计算startDeliveryTime
     *
     * @param message 消息内容
     * @return startDeliveryTime
     */
    private long calculateDeliverTime(OrderPostponeMessageDto message) {
        long maxDelayTime = RocketMQConstants.MAX_DELAY_TIME;
        long secondDelayTime = RocketMQConstants.SECOND_DELAY_TIME;
        if (isDebug()) {
            // debug模式下，业务测试最大延迟时间改为40分钟，超过最大延迟时间，重进消息队列延迟时间为39分钟
            maxDelayTime = 40 * 60 * 1000L;
            secondDelayTime = 39 * 60 * 1000L;
        }
        if (message.beyondMaxDelay(maxDelayTime)) {
            return System.currentTimeMillis() + secondDelayTime;
        } else {
            return message.getPostponeTimestamp();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<NullObject> handlePostponeMsg(OrderPostponeMessageDto message) {
        FieldChecker.checkEmpty(message, "订单套餐超期检测消息");
        message.check();
        String orderId = message.getOrderId();
        String packageId = message.getPackageId();
        // 当前订单套餐尚未超期，计算剩余超期时间，重新进入定时消息队列
        if (message.getPostponeTimestamp() > System.currentTimeMillis()) {
            LOGGER.warn("订单[{}]套餐[{}]检测时长超过40天，重新进入超期检测队列", orderId, packageId);
            sendPostponeMessage(message);
        }
        // 查询订单信息
        GeneticTestingOrder order = orderRepository.findById(orderId);
        if (order == null || DeleteFlagType.DELETE.equals(DeleteFlagType.getEnum(order.getDeleteFlag()))) {
            LOGGER.error("订单[{}]不存在", orderId);
            return ApiResult.succ();
        }
        GeneticTestingOrderPackage orderPackage = orderPackageRepository.findOrderPackage(orderId, packageId);
        // 订单套餐不存在
        if (orderPackage == null) {
            LOGGER.error("订单[{}]套餐[{}]不存在", orderId, packageId);
            return ApiResult.succ();
        }
        // 状态为待上传报告
        OrderPackageStatus status = OrderPackageStatus.getEnum(orderPackage.getStatus());
        if (!OrderPackageStatus.REPORT_UPLOAD.equals(status)) {
            LOGGER.info("订单[{}]套餐[{}]当前状态为[{}:{}]，该订单套餐没有检测超期", orderId, packageId, status.getCode(), status.getDesc());
            return ApiResult.succ();
        }
        LOGGER.warn("订单[{}]套餐[{}]当前状态为[{}:{}]，该订单套餐已经检测超期", orderId, packageId, status.getCode(), status.getDesc());
        orderPackage.setPostponeFlag(PostponeFlag.POSTPONE.getCode());
        orderPackageRepository.updateForPostpone(orderPackage);
        PostponeFlag orderPostponeFlag = PostponeFlag.getEnum(order.getPostponeFlag());
        if (PostponeFlag.NORMAL.equals(orderPostponeFlag)) {
            order.setPostponeFlag(PostponeFlag.POSTPONE.getCode());
            order.setUpdateDateTime(new Date());
            orderRepository.updateOrderForPostpone(order);
        }

        return ApiResult.succ();
    }

    @Override
    public ApiResult<NullObject> clearPostpone(String orderId, String packageId) {
        FieldChecker.checkEmpty(orderId, "订单id");
        FieldChecker.checkEmpty(packageId, "套餐id");

        // 查询订单信息
        GeneticTestingOrder order = orderRepository.findById(orderId);
        if (order == null || DeleteFlagType.DELETE.equals(DeleteFlagType.getEnum(order.getDeleteFlag()))) {
            LOGGER.error("订单[{}]不存在", orderId);
            return ApiResult.fail(OrderErrorCode.ORDER_NOT_EXISTED);
        }
        GeneticTestingOrderPackage orderPackage = orderPackageRepository.findOrderPackage(orderId, packageId);
        // 订单套餐不存在
        if (orderPackage == null) {
            LOGGER.error("订单[{}]套餐[{}]不存在", orderId, packageId);
            return ApiResult.fail(OrderErrorCode.ORDER_PACKAGE_NOT_EXISTED);
        }
        // 状态为待上传报告（未上传检测报告，不允许清除超期检测标志）
        OrderPackageStatus status = OrderPackageStatus.getEnum(orderPackage.getStatus());
        if (OrderPackageStatus.REPORT_UPLOAD.equals(status)) {
            LOGGER.error("订单[{}]套餐[{}]当前状态为[{}:{}]，不能清除超期检测标志", orderId, packageId, status.getCode(), status.getDesc());
            return ApiResult.fail(OrderErrorCode.ORDER_POSTPONE_REPORT_NOT_UPLOAD);
        }
        if (PostponeFlag.POSTPONE.equals(PostponeFlag.getEnum(orderPackage.getPostponeFlag()))) {
            // 清除超期检测标志
            orderPackage.setPostponeFlag(PostponeFlag.NORMAL.getCode());
            orderPackageRepository.updateForPostpone(orderPackage);
            LOGGER.info("订单[{}]套餐[{}]超期检测标志已清除", orderId, packageId);
        } else {
            LOGGER.info("订单[{}]套餐[{}]没有检测超期", orderId, packageId);
        }
        if (PostponeFlag.POSTPONE.equals(PostponeFlag.getEnum(order.getPostponeFlag()))) {
            // 判断订单所有套餐均已上传检测报告，清除订单超期检测标志
            List<GeneticTestingOrderPackage> allPackages = orderPackageRepository.queryOrderPackages(orderId);
            if (allPackages.stream().noneMatch(e -> OrderPackageStatus.REPORT_UPLOAD.equals(OrderPackageStatus.getEnum(e.getStatus())))) {
                order.setPostponeFlag(PostponeFlag.NORMAL.getCode());
                order.setUpdateDateTime(new Date());
                orderRepository.updateOrderForPostpone(order);
                LOGGER.info("订单[{}]超期检测标志已清除", orderId);
            }
        } else {
            LOGGER.info("订单[{}]没有检测超期", orderId);
        }

        return ApiResult.succ();
    }

    private boolean isDebug() {
        return Boolean.TRUE.equals(mqRocketDebug);
    }
}
